package com.indris.material;

import com.indris.tools.Utils;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Button;
import ohos.agp.components.Component;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.Rect;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;
import ohos.multimodalinput.event.TouchEvent;

public class RippleView extends Button implements Component.DrawTask, Component.TouchEventListener {

    /*起始点*/
    private int mDownX;
    private int mDownY;

    private float mDensity;
    /*绘制的半径*/
    private float mDrawRadius;
    /*绘制的最大半径*/
    private float mMaxRadius;
    private float mStepRadius;
    private static final int DRAW_RADIUS_ZERO = 0;
    private static final int DRAW_RADIUS_MOVE = 50;

    private boolean mDrawFinish;
    private static final int DURATION = 400;
    private static final int FREQUENCY = 10;
    private float mCycle;
    private final Rect mRect = new Rect();
    private boolean mPressUp = false;
    private Paint mRevealPaint;

    private int mRippleColor = Color.BLACK.getValue();
    private float mAlphaFactor = 0.2f;
    private boolean mHover = true;

    private AnimatorValue mRadiusAnimator;
    private int mDuration = 6000;

    private boolean isPointMove = false;

    public RippleView(Context context) {
        this(context, null);
    }

    public RippleView(Context context, AttrSet attrSet) {
        this(context, attrSet, "");
    }

    public RippleView(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        mRippleColor = Utils.getColorFromAttr(attrSet, "rippleColor", mRippleColor);
        mAlphaFactor = Utils.getFloatFromAttr(attrSet, "alphaFactor", mAlphaFactor);
        mHover = Utils.getBooleanFromAttr(attrSet, "hover", mHover);
        if (mHover) {
            initView();
            startAnimation();
            setTouchEventListener(this);
            addDrawTask(this);
            setBindStateChangedListener(new BindStateChangedListener() {
                @Override
                public void onComponentBoundToWindow(Component component) {
                    mRect.set(getLeft(), getTop(), getRight(), getBottom());
                }

                @Override
                public void onComponentUnboundFromWindow(Component component) {

                }
            });
        }
    }

    @Override
    public void addDrawTask(DrawTask task) {
        super.addDrawTask(task);
        task.onDraw(this, mCanvasForTaskOverContent);
    }

    public void initView() {
        mCycle = (float) DURATION / FREQUENCY;
        mDensity = DisplayManager.getInstance().getDefaultDisplay(getContext()).get().getAttributes().densityPixels;
        mCycle = (mDensity * mCycle);
        mDrawFinish = true;

        mRevealPaint = new Paint();
        mRevealPaint.setAntiAlias(true);
        mRevealPaint.setColor(new Color(adjustAlpha(mRippleColor, mAlphaFactor)));

        mRadiusAnimator = new AnimatorValue();
        mRadiusAnimator.setDuration(mDuration);
        mRadiusAnimator.setLoopedCount(AnimatorValue.INFINITE);
        mRadiusAnimator.setCurveType(AnimatorValue.CurveType.ACCELERATE_DECELERATE);
        mRadiusAnimator.setValueUpdateListener((animatorValue, v) -> {
            invalidate();
        });
    }

    private void startAnimation() {
        mRadiusAnimator.start();
    }

    private void updateDrawData() {
        setDrawRadius(DRAW_RADIUS_ZERO);
        /*计算最大半径*/
        int radiusLeftTop = (int) Math.sqrt((double) mDownX * mDownX + mDownY * mDownY);
        int radiusRightTop = (int) Math.sqrt((double) (mRect.right - (mDownX + getLeft())) *
                (mRect.right - (mDownX + getLeft())) +
                mDownY * mDownY);
        int radiusLeftBottom = (int) Math.sqrt((double) mDownX * mDownX +
                (mRect.getHeight() - mDownY) * (mRect.getHeight() - mDownY));
        int radiusRightBottom = (int) Math.sqrt((double) (mRect.right - (mDownX + getLeft())) *
                (mRect.right - (mDownX + getLeft())) +
                (mRect.getHeight() - mDownY) * (mRect.getHeight() - mDownY));
        mMaxRadius = Utils.getMax(radiusLeftTop, radiusRightTop, radiusLeftBottom, radiusRightBottom);

        mStepRadius = mMaxRadius / mCycle;
    }

    public int adjustAlpha(int color, float factor) {
        int alpha = Math.round(Color.alpha(color) * factor);
        int red = Utils.red(color);
        int green = Utils.green(color);
        int blue = Utils.blue(color);
        return Color.argb(alpha, red, green, blue);
    }

    private int dp(int dp) {
        return (int) (dp * mDensity + 0.5d);
    }

    public void setDrawRadius(final float radius) {
        mDrawRadius = radius;
    }

    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        int action = touchEvent.getAction();
        isPointMove = false;
        switch (action) {
            case TouchEvent.PRIMARY_POINT_DOWN: {
                if (!mHover) {
                    break;
                }

                mPressUp = false;
                mDrawFinish = false;
                mDownX = (int) touchEvent.getPointerPosition(touchEvent.getIndex()).getX();
                mDownY = (int) touchEvent.getPointerPosition(touchEvent.getIndex()).getY();
                updateDrawData();
                break;
            }
            case TouchEvent.POINT_MOVE:
                if (!mHover) {
                    break;
                }
                if (mDrawFinish) {
                    mDrawFinish = false;
                }
                isPointMove = true;
                int downX = (int) touchEvent.getPointerPosition(touchEvent.getIndex()).getX();
                int downY = (int) touchEvent.getPointerPosition(touchEvent.getIndex()).getY();
                if (!mRect.isInclude(downX + getLeft(), downY + getTop())) {
                    setDrawRadius(DRAW_RADIUS_ZERO);
                } else {
                    mDownX = downX;
                    mDownY = downY;
                    updateDrawData();
                    setDrawRadius(dp(DRAW_RADIUS_MOVE));
                }
                break;
            case TouchEvent.CANCEL:
            case TouchEvent.PRIMARY_POINT_UP:
                mStepRadius = (int) (5 * mStepRadius);
                mPressUp = true;
                if (mDrawFinish) {
                    mDrawFinish = false;
                    setDrawRadius(dp(DRAW_RADIUS_MOVE));
                }
                break;
        }
        return true;
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        if (mDrawFinish) {
            return;
        }

        canvas.drawColor(0x15000000, Canvas.PorterDuffMode.OVERLAY);

        if (mStepRadius == 0) {
            return;
        }
        if (!isPointMove) {
            mDrawRadius = (float) ((double) mDrawRadius + (double) mStepRadius);
        }

        if (mDrawRadius > dp(DRAW_RADIUS_MOVE) && !mPressUp) {
            setDrawRadius(DRAW_RADIUS_ZERO);
            mDrawFinish = true;
        }

        if (mDrawRadius > mMaxRadius) {
            setDrawRadius(DRAW_RADIUS_ZERO);
            canvas.drawCircle(mDownX, mDownY, mMaxRadius, mRevealPaint);
            mDrawFinish = true;
            return;
        }
        canvas.drawCircle(mDownX, mDownY, mDrawRadius, mRevealPaint);
    }
}
